package chess;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import chess.piece.Bishop;
import chess.piece.King;
import chess.piece.Knight;
import chess.piece.Pawn;
import chess.piece.Piece;
import chess.piece.Queen;
import chess.piece.Rook;

/**
 * Classe ChessBoard (Tabuleiro de Xadrez)
 * @author Beatriz Miho Anami
 * @author Lucas Rodrigues Anizelli
 * @author Marcos Okamura Rodrigues
 */

public class ChessBoard extends JPanel implements Constants {
	private Piece[][] board;
	private Piece[][] pieceList;
	private ArrayList<Position> checkPosition;
	private Color[][] squareColor; 
	private int playerView;
	private Piece lastWhitePawn;
	private Piece lastBlackPawn;
	private Position lastWhitePosition;
	private Position lastBlackPosition;

	private static final int NUMBER_OF_PIECES = 16;
	private static final int NUMBER_OF_PLAYERS = 2;
	private static final long serialVersionUID = 1L;
	
	/**
	 * Construtor do tabuleiro de xadrez.
	 */
	public ChessBoard(){
		super();
		this.board = new Piece[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
		this.pieceList = new Piece[NUMBER_OF_PLAYERS][NUMBER_OF_PIECES];
		this.checkPosition = new ArrayList<Position>();
		this.playerView = SPECTATOR;
		this.lastWhitePawn = null;
		this.lastBlackPawn = null;
		this.lastWhitePosition = null;
		this.lastBlackPosition = null;
		this.initGUI();
	}
	
	/**
	 * Modifica a visão do jogador.
	 * @param playerView	visão do jogador
	 */
	public void setPlayerView(int playerView){
		this.playerView = playerView;
	}
	
	/**
	 * Adiciona um jogador ao tabuleiro.
	 * @param color	cor do jogador
	 */
	public void addPlayer(int color){
		int firstRow, secondRow;
		String pieceColor;
		if(color == WHITE){
			firstRow = Position.FIRST_WHITE_ROW;
			secondRow = Position.SECOND_WHITE_ROW;
			pieceColor = "White.png";
		} else {
			firstRow = Position.FIRST_BLACK_ROW;
			secondRow = Position.SECOND_BLACK_ROW;
			pieceColor = "Black.png";
		}
		
		// configuração inicial das peças
		this.pieceList[color][Piece.ROOK_1] = new Rook(Piece.ROOK_1, new Position(firstRow, 0), color, 
			new ImageIcon(getClass().getResource("/image/Rook" + pieceColor)));
		this.pieceList[color][Piece.KNIGHT_1] = new Knight(Piece.KNIGHT_1, new Position(firstRow, 1), color,
			new ImageIcon(getClass().getResource("/image/Knight" + pieceColor)));
		this.pieceList[color][Piece.BISHOP_1] = new Bishop(Piece.BISHOP_1, new Position(firstRow, 2), color, 
			new ImageIcon(getClass().getResource("/image/Bishop" + pieceColor)));
		this.pieceList[color][Piece.QUEEN] = new Queen(Piece.QUEEN, new Position(firstRow, 3), color, 
			new ImageIcon(getClass().getResource("/image/Queen" + pieceColor)));
		this.pieceList[color][Piece.KING] = new King(Piece.KING, new Position(firstRow, 4), color, 
			new ImageIcon(getClass().getResource("/image/King" + pieceColor)));
		this.pieceList[color][Piece.BISHOP_2] = new Bishop(Piece.BISHOP_2, new Position(firstRow, 5), color,
			new ImageIcon(getClass().getResource("/image/Bishop" + pieceColor)));
		this.pieceList[color][Piece.KNIGHT_2] = new Knight(Piece.KNIGHT_2, new Position(firstRow, 6), color,
			new ImageIcon(getClass().getResource("/image/Knight" + pieceColor)));
		this.pieceList[color][Piece.ROOK_2] = new Rook(Piece.ROOK_2, new Position(firstRow, 7), color, 
			new ImageIcon(getClass().getResource("/image/Rook" + pieceColor)));
		for(int i = 8; i < NUMBER_OF_PIECES; i++){
			this.pieceList[color][i] = new Pawn(i, new Position(secondRow, i % 8), color, 
				new ImageIcon(getClass().getResource("/image/Pawn"+pieceColor)));
		}
		for(int i = 0; i < NUMBER_OF_PIECES; i++){
			this.setPiece(this.pieceList[color][i], pieceList[color][i].getPosition());
		} 
	}
	
	/**
	 * Verifica se é possível efetuar alguma jogada entre as posições selecionadas pelo jogador especificado.
	 * Caso seja possível efetuar a jogada, retorna uma string com sua descrição.
	 * @param color				cor do jogador que tenta efetuar o movimento
	 * @param previousPosition	posição inicial do movimento
	 * @param nextPosition		posição final do movimento
	 * @param promoteLetter		letra da peça a ser promovida
	 * @return					descrição do movimento se é possível efetuar algum ou uma string vazia caso contrário
	 */
	public String tryMove(int color, Position previousPosition, Position nextPosition, char promoteLetter){
		String action = new String();
		Piece firstPiece = this.getPiece(previousPosition);
		if((firstPiece != null) && (firstPiece.getColor() == color)){
			if(!isIllegalMove(color, previousPosition, nextPosition)){
				Piece lastPiece = this.getPiece(nextPosition); 
				if(lastPiece == null){
					// movimento
					if(this.canMove(firstPiece, nextPosition)){
						Status status = firstPiece.canMove(nextPosition);
						if(status == Status.CHECK_SHORT_CASTLING){
							action = this.shortCastle(color);
						} else if(status == Status.CHECK_LONG_CASTLING){
							action = this.longCastle(color);
						} else if(status == Status.CHECK_EN_PASSANT){
							action = this.enPassant((Pawn) firstPiece, nextPosition);
						} else {
							action = this.move(firstPiece, nextPosition);
						}
					}
				} else {
					if((firstPiece.getColor() != lastPiece.getColor())){
						// captura
						if(this.canCapture(firstPiece, nextPosition)){
							action = this.capture(firstPiece, lastPiece);
						}
					}
				}
				// promoção
				if(!(action.isEmpty()) && firstPiece instanceof Pawn){
					if(((Pawn) firstPiece).canPromote()){
						if(promoteLetter == NO_LETTER){
							action += this.promote((Pawn)firstPiece, this.choosePiece(color));
						} else {
							action += this.promote((Pawn)firstPiece, promoteLetter);
						}
					}
				}
			}
		}
		
		// peão movido na última rodada para verificar en passant
		if(!action.isEmpty()){
			this.updateLastPawnMove(color, firstPiece, previousPosition);
		}
		this.highlightCheck(color);
		return action;
	}
	
	/**
	 * Verifica se o jogador especificado está em xeque.
	 * @param color	cor do jogador a ser verificado
	 * @return		true se o jogador está em xeque ou false caso contrário
	 */
	public boolean isCheck(int color){
		checkPosition.clear();
		int oppositeIndex = 1 - color;
		Position position = pieceList[color][Piece.KING].getPosition();
		for(int i = 0; i < NUMBER_OF_PIECES; i++){
			if(pieceList[oppositeIndex][i] != null && this.canCapture(pieceList[oppositeIndex][i], position)){
				checkPosition.add(pieceList[oppositeIndex][i].getPosition());
			}
		}
		return !(checkPosition.isEmpty());
	}
	
	/**
	 * Verifica se o xeque é duplo.
	 * @return	true se o xeque é duplo ou false caso contrário
	 */
	public boolean isDoubleCheck(){
		return (checkPosition.size() == 2);
	}

	/**
	 * Verifica se o jogador especificado está em xeque-mate.
	 * @param color	cor do jogador a ser verificado
	 * @return		true se o jogador está em xeque-mate ou false caso contrário
	 */
	public boolean isCheckMate(int color){
		if(this.isDoubleCheck()){
			return (this.getRange(pieceList[color][Piece.KING]).size() == 0);
		} else {
			Position position = checkPosition.get(0);
			if(this.getRange(pieceList[color][Piece.KING]).size() != 0 || this.canCapture(color, position)){
				return false;
			} else {
				Position kingPosition = pieceList[color][Piece.KING].getPosition();
				Status status = this.getPiece(position).canCapture(kingPosition);
				switch(status){
					case YES: 
						return true;
					case CHECK_EN_PASSANT:
						return true;
					case CHECK_ROW: 
						return !(this.canBlockPath(color, Position.getRowPath(position, kingPosition))); 
					case CHECK_COLUMN:
						return !(this.canBlockPath(color, Position.getColumnPath(position, kingPosition)));
					case CHECK_LEFT_DIAGONAL:
						return !(this.canBlockPath(color, Position.getLeftDiagonalPath(position, kingPosition)));
					default:
						return !(this.canBlockPath(color, Position.getRightDiagonalPath(position, kingPosition)));
				}
			}
		}
	}

	/**
	 * Verifica se o jogador especificado está em rei afogado.
	 * @param color	cor dojogador a ser verificado
	 * @return		true se o jogador está em rei afogado ou false caso contrário
	 */
	public boolean isStaleMate(int color){
		for(int i = 0; i < NUMBER_OF_PIECES; i++){
			Piece piece = pieceList[color][i];
			if(piece != null && this.getRange(piece).size() != 0){
				return false;
			}
		} 
		return true;
	}
	
	/**
	 * Tenta selecionar a peça na posição selecionada
	 * @param player	jogador que deseja efetuar a seleção
	 * @param position	posição da peça selecionada
	 * @return			true se a peça foi selecionado ou false caso contrário
	 */
	public boolean trySelectPiece(int player, Position position){
		Piece piece = this.getPiece(position);
		if(piece != null && player == piece.getColor()){
			this.highlightSelect(piece);
			return true;
		} else {
			this.highlightCheck(1 - player);
			return false;
		}
		
	}
	
	/**
	 * Desenha o tabuleiro.
	 * @param graphics	gráfico do desenho
	 **/
	@Override
	public void paint(Graphics graphics){
		super.paint(graphics);
		if(playerView == WHITE || playerView == SPECTATOR){
			this.paintWhiteView(graphics);
		} else {
			this.paintBlackView(graphics);
		}
	}

	/**
	 * Retorna a referência da peça da posição do tabuleiro selecionada.
	 * @param position	posição do tabuleiro
	 * @return			referência da peça da posição ou null caso contrário
	 */
	private Piece getPiece(Position position){
		return this.board[position.getRow()][position.getColumn()];
	}
	
	/**
	 * Modifica a referência da posição selecionada do tabuleiro para a peça especificada.
	 * @param piece		peça a ser referida
	 * @param position	posição do tabuleiro
	 */
	private void setPiece(Piece piece, Position position){
		this.board[position.getRow()][position.getColumn()] = piece;
	}
	
	/**
	 * Remove a peça selecionada do jogo.
	 * @param piece	peça a ser removida
	 */
	private void removePiece(Piece piece){
		this.setPiece(null, piece.getPosition());
		pieceList[piece.getColor()][piece.getId()] = null;
	}
	
	/**
	 * Verifica se o trajeto está vazio.
	 * @param arrayList	vetor com as posições do trajeto
	 * @return			true se o trajeto está vazio ou false caso contrário
	 */
	private boolean isEmptyPath(ArrayList<Position> arrayList){
		int count = 0;
		for(int i = 0; i < arrayList.size(); i++){
			Piece piece = this.getPiece(arrayList.get(i));
			if(piece != null){
				count++;
			}
		}
		return (count == 0);
	}
	
	/**
	 * Verifica se o trajeto está vazio.
	 * @param arrayList	vetor com as posições do trajeto
	 * @param position	posição desconsiderada durante a execução do método
	 * @return			true se o trajeto está vazio ou false caso contrário
	 */
	private boolean isEmptyPath(ArrayList<Position> arrayList, Position position){
		int count = 0;
		for(int i = 0; i < arrayList.size(); i++){
			Piece piece = this.getPiece(arrayList.get(i));
			if(piece != null && !(arrayList.get(i).equals(position))){
				count++;
			}
		}
		return (count == 0);
	}
	
	/**
	 * Verifica se a jogada é impossível (inválida).
	 * @param color				jogador
	 * @param previousPosition	posição anterior da peça
	 * @param nextPosition		próxima posição após o movimento
	 * @return					true se a jogada é impossível ou false caso contrário
	 */
	private boolean isIllegalMove(int color, Position previousPosition, Position nextPosition){
		Position kingPosition = pieceList[color][Piece.KING].getPosition();
		// deixar o próprio rei em xeque
		int piecePath = Position.getPathType(previousPosition, nextPosition);
		for(int i = 0; i < NUMBER_OF_PIECES; i++){
			Piece piece = pieceList[1 - color][i];
			if(piece != null && this.canCapture(piece, previousPosition)){
				if(piece instanceof Queen || piece instanceof Rook || piece instanceof Bishop){
					int checkPath = Position.getPathType(kingPosition, piece.getPosition());
					int blockPath = Position.getPathType(previousPosition, kingPosition);
					if(checkPath != Position.UNKNOWN_PATH && piecePath != checkPath && blockPath == checkPath){
						if(this.isEmptyPath(Position.getPath(piece.getPosition(), kingPosition), previousPosition)){
							return true;
						}
					}
				}
			}
		}
			
		// cancelar o xeque adversário
		if(this.isCheck(color)){
			if(this.getPiece(previousPosition) instanceof King){
				return false;
			} else {
				if(!this.isDoubleCheck()){
					Position position = checkPosition.get(0);
					if(nextPosition.equals(position)){
						return false;
					} else {
						ArrayList<Position> path = Position.getPath(position, kingPosition);
						for(int i = 0; i < path.size(); i++){
							if(path.get(i).equals(nextPosition)){
								return false;
							}
						}
					}
				}
			}
			return true;
		} else {
			return false;
		}
		
	}
	
	/**
	 * Verifica se o jogador especificado consegue bloquear o trajeto.
	 * @param color		cor do jogador
	 * @param arrayList	vetor com as posições do trajeto
	 * @return			true se o jogador consegue bloquear o trajeto ou false caso contrário
	 */
	private boolean canBlockPath(int color, ArrayList<Position> arrayList){
		for(int i = 0; i < arrayList.size(); i++){
			for(int j = 0; j < NUMBER_OF_PIECES; j++){
				if(pieceList[color][j] != null && this.canMove(pieceList[color][j], arrayList.get(i))){
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * Verifica se a peça pode ser movida até a posição especificada.
	 * @param piece		peça selecionada
	 * @param position	posição de destino
	 * @return			true se a peça pode ser movida ou false caso contrário
	 */
	private boolean canMove(Piece piece, Position position){
		if(piece.getPosition().equals(position)){
			return false;
		}
		Status status = piece.canMove(position);
		switch(status){
			case YES: 
				return true;
			case CHECK_ROW: 
				return this.isEmptyPath(Position.getRowPath(piece.getPosition(), position));
			case CHECK_COLUMN:
				return this.isEmptyPath(Position.getColumnPath(piece.getPosition(), position));
			case CHECK_LEFT_DIAGONAL:
				return this.isEmptyPath(Position.getLeftDiagonalPath(piece.getPosition(), position));
			case CHECK_RIGHT_DIAGONAL:
				return this.isEmptyPath(Position.getRightDiagonalPath(piece.getPosition(), position));
			case CHECK_UNDER_ATTACK:
				return !(this.canAttack(1 - piece.getColor(), position));
			case CHECK_SHORT_CASTLING:
				return this.canShortCastle(piece.getColor());
			case CHECK_LONG_CASTLING:
				return this.canLongCastle(piece.getColor());
			case CHECK_EN_PASSANT:
				if(piece.getColor() == WHITE){
					if(lastBlackPawn != null){
						return ((Pawn) piece).canEnPassant(lastBlackPosition, lastBlackPawn.getPosition(), position);
					}
				} else {
					if(lastWhitePawn != null){
						return ((Pawn) piece).canEnPassant(lastWhitePosition, lastWhitePawn.getPosition(), position);
					}
				}
			default:
				return false;
		}
	}
	
	/**
	 * Verifica se a peça pode capturar na posição especificada.
	 * @param piece		peça selecionada
	 * @param position	posição da captura
	 * @return			true se a peça pode efetuar a captura ou false caso contrário.
	 */
	private boolean canCapture(Piece piece, Position position){
		if(piece.getPosition().equals(position)){
			return false;
		}
		Status status = piece.canCapture(position);
		switch(status){
			case YES: 
				return true;
			case CHECK_ROW: 
				return this.isEmptyPath(Position.getRowPath(piece.getPosition(), position));
			case CHECK_COLUMN:
				return this.isEmptyPath(Position.getColumnPath(piece.getPosition(), position));
			case CHECK_LEFT_DIAGONAL:
				return this.isEmptyPath(Position.getLeftDiagonalPath(piece.getPosition(), position));
			case CHECK_RIGHT_DIAGONAL:
				return this.isEmptyPath(Position.getRightDiagonalPath(piece.getPosition(), position));
			case CHECK_UNDER_ATTACK:
				return !(this.canAttack(1 - piece.getColor(), position));
			default:
				return false;
		}
	}
	
	/**
	 * Verifica se a peça pode atacar a posição especificada.
	 * @param piece		peça selecionada
	 * @param position	posição de ataque
	 * @return			true se a peça pode atacar a posição ou false caso contrário.
	 */
	private boolean canAttack(Piece piece, Position position){
		if(piece.getPosition().equals(position)){
			return false;
		}
		Status status = piece.canCapture(position);
		Position kingPosition = pieceList[1 - piece.getColor()][Piece.KING].getPosition();
		switch(status){
			case YES: 
				return true;
			case CHECK_ROW: 
				return this.isEmptyPath(Position.getRowPath(piece.getPosition(), position), kingPosition);
			case CHECK_COLUMN:
				return this.isEmptyPath(Position.getColumnPath(piece.getPosition(), position), kingPosition);
			case CHECK_LEFT_DIAGONAL:
				return this.isEmptyPath(Position.getLeftDiagonalPath(piece.getPosition(), position), kingPosition);
			case CHECK_RIGHT_DIAGONAL:
				return this.isEmptyPath(Position.getRightDiagonalPath(piece.getPosition(), position), kingPosition);
			case CHECK_UNDER_ATTACK:
				return true;
			default:
				return false;
		}
	}
	
	/**
	 * Verifica se o jogador pode atacar a posição especificada.
	 * @param color		cor do jogador
	 * @param position	posição a ser atacada
	 * @return			true se o jogador pode atacar ou false caso contrário
	 */
	private boolean canAttack(int color, Position position){
		for(int i = 0; i < NUMBER_OF_PIECES; i++){
			if(pieceList[color][i] != null && this.canAttack(pieceList[color][i], position)){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Verifica se o jogador pode capturar na posição especificada.
	 * @param color		cor do jogador
	 * @param position	posição a ser atacada
	 * @return			true se o jogador pode atacar ou false caso contrário
	 */
	private boolean canCapture(int color, Position position){
		for(int i = 0; i < NUMBER_OF_PIECES; i++){
			if(pieceList[color][i] != null && this.canCapture(pieceList[color][i], position)){
				if(this.getPiece(position).getColor() != color){
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * Verifica se o jogador especificado pode realizar o roque pequeno.
	 * @param color	cor do jogador selecionado
	 * @return		true se o roque pode ser realizado ou false caso contrário
	 */
	private boolean canShortCastle(int color){
		King king = (King) pieceList[color][Piece.KING];
		Rook rook = (Rook) pieceList[color][Piece.ROOK_2];
		if(rook == null || king.hasMoved() || rook.hasMoved()){
			return false;
		}
		int row;
		if(color == WHITE){
			row = Position.FIRST_WHITE_ROW;
		} else {
			row = Position.FIRST_BLACK_ROW;
		}
		if(this.getPiece(new Position(row, Piece.BISHOP_2)) != null
			|| this.getPiece(new Position(row, Piece.KNIGHT_2)) != null){
			return false;
		}
		if(this.canAttack(1 - color, new Position(row, Piece.BISHOP_2))
			|| this.canAttack(1 - color, new Position(row, Piece.KNIGHT_2))){
			return false;
		}
		return !(isCheck(color));
	}
	
	/**
	 * Verifica se o jogador especificado pode realizar o roque grande.
	 * @param color	cor do jogador selecionado
	 * @return		true se o roque pode ser realizado ou false caso contrário
	 */
	private boolean canLongCastle(int color){
		King king = (King) pieceList[color][Piece.KING];
		Rook rook = (Rook) pieceList[color][Piece.ROOK_1];
		if(rook == null || king.hasMoved() || rook.hasMoved()){
			return false;
		}
		int row;
		if(color == WHITE){
			row = Position.FIRST_WHITE_ROW;
		} else {
			row = Position.FIRST_BLACK_ROW;
		}
		if(this.getPiece(new Position(row, Piece.QUEEN)) != null
			|| this.getPiece(new Position(row, Piece.BISHOP_1)) != null
			|| this.getPiece(new Position(row, Piece.KNIGHT_1)) != null){
				return false;
		}
		if(this.canAttack(1 - color, new Position(row, Piece.QUEEN))
			|| this.canAttack(1 - color, new Position(row, Piece.BISHOP_1))){
			return false;
		}
		return !(isCheck(color));
	}
	
	/**
	 * Retorna o alcance da peça.
	 * @param piece	peça selecionada
	 * @return		ArrayList com as posições do alcance da peça
	 */
	private ArrayList<Position> getRange(Piece piece){
		ArrayList<Position> range = new ArrayList<Position>();
		ArrayList<Position> arrayList = piece.getRange();
		for(int i = 0; i < arrayList.size(); i++){
			Position position = arrayList.get(i);
			if(!this.isIllegalMove(piece.getColor(), piece.getPosition(), position)){
				if(this.getPiece(position) == null){
					if(this.canMove(piece, position)){
						range.add(position);
					}
				} else {
					if(this.getPiece(position).getColor() != piece.getColor()){
						if(this.canCapture(piece, position)){
							range.add(position);
						}
					}
				}
			}
		}
		return range;
	}
	
	/**
	 * Move a peça para a posição selecionada.
	 * @param piece 	peça a ser movida
	 * @param position 	nova posição da peça
	 * @return			a descrição do movimento
	 */
	private String move(Piece piece, Position position){
		String move = piece.toString() + piece.getPosition().toString()
			+ "-" + position.toString();
		this.setPiece(piece, position);
		this.setPiece(null, piece.getPosition());
		piece.setPosition(position);
		return move;
	}

	/**
	 * Realiza a captura entre as peças especificadas.
	 * @param firstPiece	peça que efetua a captura
	 * @param lastPiece		peça a ser capturada
	 * @return				a descrição da captura
	 */
	private String capture(Piece firstPiece, Piece lastPiece){
		String move = firstPiece.toString() + firstPiece.getPosition().toString()
			+ "x" + lastPiece.getPosition().toString();
		Position position = lastPiece.getPosition();
		this.removePiece(lastPiece);
		this.move(firstPiece, position);
		return move;
	}
	
	/**
	 * Realiza o en passant do peão na posição selecionada.
	 * @param pawn		peão que efetua a captura
	 * @param position	posição da captura
	 * @return			a descrição do en passant
	 */
	private String enPassant(Pawn pawn, Position position){
		String move = pawn.getPosition().toString() + "x" + position.toString() + " e.p.";
		if(pawn.getColor() == WHITE){
			this.removePiece(lastBlackPawn);
			this.move(pawn, position);
		} else {
			this.removePiece(lastWhitePawn);
			this.move(pawn, position);
		}
		return move;
	}
	
	/**
	 * Promove o peão selecionado.
	 * @param pawn	peão a ser promovido
	 * @return		a descrição da promoção
	 */
	private String promote(Pawn pawn, char pieceLetter){
		Piece piece;
		String pieceColor;
		
		if(pawn.getColor() == WHITE){
			pieceColor = "White.png";
		} else { 
			pieceColor = "Black.png";
		}
		switch(pieceLetter){
			case 'N':
				piece = new Knight(pawn.getId(), pawn.getPosition(), pawn.getColor(), 
					new ImageIcon(getClass().getResource("/image/Knight" + pieceColor)));
				break;
			case 'B' :
				piece = new Bishop(pawn.getId(), pawn.getPosition(), pawn.getColor(), 
					new ImageIcon(getClass().getResource("/image/Bishop" + pieceColor)));
				break;
			case 'R':
				piece = new Rook(pawn.getId(), pawn.getPosition(), pawn.getColor(), 
					new ImageIcon(getClass().getResource("/image/Rook" + pieceColor)));
				break;
			default:
				piece = new Queen(pawn.getId(), pawn.getPosition(), pawn.getColor(), 
					new ImageIcon(getClass().getResource("/image/Queen" + pieceColor)));
				break;
		}
		
		this.removePiece(pawn);
		this.setPiece(piece, piece.getPosition());
		pieceList[piece.getColor()][piece.getId()] = piece;
		return "=" + piece.toString();
	}
	
	/**
	 * Realiza o roque pequeno do jogador especificado.
	 * @param color	cor do jogador selecionado
	 * @return		a descrição do roque
	 */
	private String shortCastle(int color){
		int row;
		if(color == WHITE){
			row = Position.FIRST_WHITE_ROW;
		} else {
			row = Position.FIRST_BLACK_ROW;
		}
		this.move(pieceList[color][Piece.KING], new Position(row, Piece.KNIGHT_2));
		this.move(pieceList[color][Piece.ROOK_2], new Position(row, Piece.BISHOP_2));
		return "O-O";
	}
	
	/**
	 * Realiza o roque grande do jogador especificado.
	 * @param color	cor do jogador selecionado
	 * @return		a descrição do roque
	 */
	private String longCastle(int color){
		int row;
		if(color == WHITE){
			row = Position.FIRST_WHITE_ROW;
		} else {
			row = Position.FIRST_BLACK_ROW;
		}
		this.move(pieceList[color][Piece.KING], new Position(row, Piece.BISHOP_1));
		this.move(pieceList[color][Piece.ROOK_1], new Position(row, Piece.QUEEN));
		return "O-O-O";
	}
	
	/**
	 * Atualiza o último peão movido.
	 * @param color		cor do jogador
	 * @param piece		última peça movida
	 * @param position	posição anterior da peça
	 */
	private void updateLastPawnMove(int color, Piece piece, Position position){
		if(color == WHITE){
			if(piece instanceof Pawn){
				lastWhitePawn = piece;
				lastWhitePosition = position;
			} else {
				lastWhitePawn = null;
				lastWhitePosition = null;
			}
		} else {
			if(piece instanceof Pawn){
				lastBlackPawn = piece;
				lastBlackPosition = position;
			} else {
				lastBlackPawn = null;
				lastBlackPosition = null;
			}
		}
	}
	
	/**
	 * Inicializa as configurações gráficas.
	 */
	private void initGUI(){
		this.squareColor = new Color[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
		this.clearBoard();
		this.setVisible(true);
	}
	
	/**
	 * Limpa o tabuleiro.
	 */
	private void clearBoard(){
		for(int row = 0; row < NUMBER_OF_ROWS; row++){
			for(int column = 0; column < NUMBER_OF_COLUMNS; column++){
				squareColor[row][column] = new Color(0, 0, 0, 0);
			}
		}
	}
	
	/**
	 * Desenha o tabuleiro destacando a peça selecionada.
	 * @param piece	peça selecionada
	 */
	private void highlightSelect(Piece piece){
		this.clearBoard();
		int row = piece.getPosition().getRow();
		int column = piece.getPosition().getColumn();
		if(piece.getColor() == WHITE){
			squareColor[row][column] = new Color(0, 0, 191, 255);
		} else {
			squareColor[row][column] = new Color(0, 191, 0, 255);
		}
		
		ArrayList<Position> range = this.getRange(piece);
		for(int i = 0; i < range.size(); i++){
			row = range.get(i).getRow();
			column = range.get(i).getColumn();
			if(piece.getColor() == WHITE){
				squareColor[row][column] = new Color(0, 0, 191, 127);
			} else {
				squareColor[row][column] = new Color(0, 191, 0, 127);
			}
		}
		this.repaint();
	}
	
	/** 
	 * Desenha o tabuleiro destacando as peças que geram o xeque.
	 * @param player	jogador que gera o xeque
	 */
	private void highlightCheck(int player){
		this.clearBoard();
		this.isCheck(1 - player);
		for(int i = 0; i < checkPosition.size(); i++){
			Position position = checkPosition.get(i);
			int color = 1 - this.getPiece(position).getColor();
			Position kingPosition = pieceList[color][Piece.KING].getPosition();
			squareColor[position.getRow()][position.getColumn()] = new Color(191, 0, 0, 255);
			squareColor[kingPosition.getRow()][kingPosition.getColumn()] = new Color(191, 0, 0, 255);
			
			ArrayList<Position> path = Position.getPath(position, kingPosition);
			for(int j = 0; j < path.size(); j++){
				int row = path.get(j).getRow();
				int column = path.get(j).getColumn();
				squareColor[row][column] = new Color(191, 0, 0, 127);
			}
		}
		this.repaint();
	}
	
	/**
	 * Desenha o tabuleiro na visão do jogador das brancas.
	 * @param graphics	gráfico do desenho
	 */
	private void paintWhiteView(Graphics graphics){
		int squareSize = this.getHeight() / NUMBER_OF_ROWS;
		for(int row = 0; row < NUMBER_OF_ROWS; row++){
			for(int column = 0; column < NUMBER_OF_COLUMNS; column++){
				if((row + column) % 2 == 0){
					graphics.setColor(Color.BLACK);
				} else {
					graphics.setColor(Color.WHITE);
				}
				graphics.fillRect(column * squareSize, (7 - row) * squareSize, squareSize, squareSize);
				graphics.setColor(squareColor[row][column]);
				graphics.fillRect(column * squareSize, (7 - row) * squareSize, squareSize, squareSize);
				if(board[row][column] != null){
					graphics.drawImage(board[row][column].getImageIcon().getImage(), column * squareSize, (7 - row) * squareSize, squareSize, squareSize, null);
				} 
			}
		}
	}
	
	/**
	 * Desenha o tabuleiro na visão do jogador das negras.
	 * @param graphics	gráfico do desenho
	 */
	private void paintBlackView(Graphics graphics){
		int squareSize = this.getHeight() / NUMBER_OF_ROWS;
		for(int row = 0; row < NUMBER_OF_ROWS; row++){
			for(int column = 0; column < NUMBER_OF_COLUMNS; column++){
				if((row + column) % 2 == 0){
					graphics.setColor(Color.BLACK);
				} else {
					graphics.setColor(Color.WHITE);
				}
				graphics.fillRect((7 - column) * squareSize, row * squareSize, squareSize, squareSize);
				graphics.setColor(squareColor[row][column]);
				graphics.fillRect((7 - column) * squareSize, row * squareSize, squareSize, squareSize);
				if(board[row][column] != null){
					graphics.drawImage(board[row][column].getImageIcon().getImage(), (7 - column) * squareSize, row * squareSize, squareSize, squareSize, null);
				}
			}
		}
	}
	
	/**
	 * Retorna a string que indica a peça escolhida durante a promoção.
	 * @param player	jogador que efetua a promoção
	 * @return			string com o nome da peça escolhida
	 */
	private char choosePiece(int player){
		char[] letter = {'Q', 'R', 'B', 'N'};
		String[] options = { "Queen", "Rook", "Bishop", "Knight"};
		int chosenPiece = JOptionPane.showOptionDialog(this, "Choose your piece:", "Promotion", JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
		if(chosenPiece == JOptionPane.CLOSED_OPTION){
			chosenPiece = 0;
		}
		return letter[chosenPiece];
	}
}
